<?php

namespace App\Console\Commands;

use Illuminate\Support\Str;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

class CreateModel extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:create-model {--table=} {--connect=}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '创建模型';


    /**
     * Execute the console command.
     */
    public function handle()
    {
        $config = config('database');
        $connects = $config['connections'] ?? [];
        // 获取传入的参数
        $table = $this->option('table');
        $connect = $this->option('connect') ?? $config['default'];
        if (!$table) {
            $this->all($connects);
        } elseif (isset($connects[$connect])) {
            $this->one($connects[$connect], $connect, $table);
        }
    }

    /**
     * @param array $config
     * @param string $connect
     * @param string $table
     * @return void
     */
    protected function one(array $config, string $connect, string $table): void
    {
        if ($config['driver'] == 'mysql') {
            $sql = sprintf(
                "SELECT TABLE_NAME,TABLE_COMMENT From information_schema.`TABLES` where table_schema = '%s' and TABLE_TYPE = 'BASE TABLE' and TABLE_NAME = '%s'",
                $config['database'], $table
            );
            $result = DB::connection($connect)->select($sql);
            if (isset($result[0])) {
                $this->mysqlTable($connect, $config['database'], $result[0]);
            }
        }
    }

    /**
     * @param array $configs
     * @return void
     */
    protected function all(array $configs): void
    {
        foreach ($configs as $connect => $config) {
            // mysql
            if ($config['driver'] == 'mysql') {
                $this->mysql($connect, $config);
            }
        }
    }

    /**
     * @param string $connect
     * @param string $database
     * @param array $table
     * @return void
     */
    protected function mysqlTable(string $connect, string $database, $table): void
    {
        $columns = DB::connection($connect)->select(sprintf(
            "SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT From information_schema.`columns` where table_schema = '%s' and table_name = '%s'",
            $database, $table->TABLE_NAME
        ));
        $fillable = "";
        $properties = "";
        foreach ($columns as $column) {
            $fillable .= "'" . $column->COLUMN_NAME . "', ";
            $properties .= sprintf(
                ' @property %s $%s %s' . PHP_EOL . "*",
                $this->dataType2PhpType($column->DATA_TYPE),
                $column->COLUMN_NAME,
                $column->COLUMN_COMMENT
            );
        }
        $namespace = 'App\\Models';
        $modelName = Str::studly($table->TABLE_NAME);
        $tpl = file_get_contents(app()->basePath("/resources/tpl/Model.tpl"));
        $model = str_replace(
            ['{namespace}','{model}','{comment}', '{property}', '{connection}', '{tableName}', '{fillable}'],
            [$namespace, $modelName, $table->TABLE_COMMENT, $properties, $connect, $table->TABLE_NAME, substr($fillable, 0, strrpos($fillable, ", "))],
            $tpl
        );
        // 写入
        $this->writeModel(app()->basePath('/app/Models/'.$modelName.'.php'), $model);
    }

    /**
     * @param string $connect
     * @param array $config
     * @return void
     */
    protected function mysql(string $connect, array $config): void
    {
        $result = DB::connection($connect)->select(sprintf(
            "SELECT TABLE_NAME,TABLE_COMMENT From information_schema.`TABLES` where table_schema = '%s' and TABLE_TYPE = 'BASE TABLE'",
            $config['database']
        ));

        foreach ($result as $table) {
            $this->mysqlTable($connect, $config['database'], $table);
        }
    }

    /**
     * @param string $path
     * @param string $content
     * @return void
     */
    protected function writeModel(string $path, string $content): void
    {
        $dirs = pathinfo($path, PATHINFO_DIRNAME);
        if (!is_dir($dirs)) {
            File::makeDirectory($dirs);
        }
        if (file_exists($path)) {
            $this->info(sprintf("%s 文件已存在,进行覆盖", $path));
        }
        File::replace($path, $content);
        $this->info(sprintf("%s 生成成功", $path));
    }

    /**
     * @param string $dataType
     * @return string
     */
    protected function dataType2PhpType(string $dataType): string
    {
        return match($dataType) {
            "int", "tinyint" => "int",
            "decimal" => "float",
            default => "string",
        };
    }
}
